#include<iostream>

#include<stdio.h>

#include<string.h>

#include <stdlib.h>

#include<unistd.h>

#include <sys/wait.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#define PATH_MAX 1024

#define INPUT_MAX 1024

#define NONE -1

#define IN_RDIR 0

#define OUT_RDIR 1

#define APPEND_RDIR 2

using namespace std;

char myenv[INPUT_MAX][INPUT_MAX] = {0};

int myenv_sz = 0;

int lastcode = 0;

char *rdirfilename = NULL;

int rdir = NONE;

void check_redir(char *cmd)
{
    while(*cmd)
    {
        if(*cmd == '>')
        {
            if(*(cmd+1) != '>')
            {
                *cmd = '\0';
                cmd++;
                while(*cmd == ' ')
                {
                    cmd++;
                }

                rdirfilename = cmd;
                rdir = OUT_RDIR;
                break;
            }
            else
            {
                *cmd = '\0';
                cmd++;
                *cmd = '\0';
                cmd++;
                while(*cmd == ' ')
                {
                  ++cmd;
                }
                rdirfilename = cmd;
                rdir = APPEND_RDIR;
                break;
            }
        }
        else if(*cmd == '<')
        {
            *cmd = '\0';
            cmd++;
            while(*cmd == ' ')
            {
                ++cmd;
            }
            rdirfilename = cmd;
            rdir = IN_RDIR;
        }
        cmd++;
    }
}

void Print_Menu()
{
    char arr_cwd[PATH_MAX];
    getcwd(arr_cwd, PATH_MAX);
    char* pm_token = strtok(arr_cwd, "/");
    char* prev_pm_token = NULL;
    char tmp[] = "/";
    while (pm_token)
    {
        prev_pm_token = pm_token;
        pm_token = strtok(NULL, "/");
    }
    if (!prev_pm_token) prev_pm_token = tmp;
    cout << "mybash:[" << getenv("USER") << "@" << getenv("HOSTNAME") << " " << prev_pm_token << "]"<< (strcmp(getenv("USER"), "root") ? "$ " : "# ");
}

int My_Cd(char* str)
{
    setenv("PWD", str, 1);
    return chdir(str);
}

int My_Export(char* str)
{
   // char* tmp = new char[INPUT_MAX];
   // strcpy(tmp, str);
   // myenv[myenv_sz++] = tmp;
    
    strcpy(myenv[myenv_sz++], str);
    return putenv(myenv[myenv_sz - 1]);
}

int My_Echo(char* str)
{
    if(strcmp(str, "$?") == 0)
    {
        printf("%d\n", lastcode);
        lastcode = 0;
    }
    else if(*str == '$')
    {
        char* val = getenv(str+1);
        if(val) printf("%s\n", val);
    }
    else
    {
        printf("%s\n", str);
    }
    return 0;
}

int Partition(char* AR[], char AI[])
{
    fgets(AI, INPUT_MAX, stdin);
    size_t len = strlen(AI);
    if (len > 0 && AI[len - 1] == '\n')
    {
      AI[len - 1] = '\0';
    }

    check_redir(AI);

    int i = 0;
    AR[i++] = strtok(AI, " ");
    while (AR[i++] = strtok(NULL, " ")){}
    return i - 1;
}

int Branch(char** AR, int sz)
{
    if (strcmp(AR[0], "cd") == 0 && sz == 2)
    {
        return My_Cd(AR[1]);
    }
    else if(strcmp(AR[0], "export") == 0 && sz == 2)
    {
        return My_Export(AR[1]);
    }
    else if(strcmp(AR[0], "echo") == 0 && sz == 2)
    {
        return My_Echo(AR[1]);
    }
    else
    {
        int ret = fork();
        if (ret == 0)
        {
            if(rdir == OUT_RDIR)
            {
                int fd = open(rdirfilename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
                dup2(fd, 1);
            }
            else if(rdir == APPEND_RDIR)
            {
                int fd = open(rdirfilename, O_WRONLY|O_CREAT|O_APPEND, 0666);
                dup2(fd, 1);
            }
            else if(rdir == IN_RDIR)
            {
                int fd = open(rdirfilename, O_RDONLY);
                dup2(fd, 0);
            }

            execvp(AR[0], AR);
            exit(0);
        }
        else if (ret > 0)
        {
            int st = 0;
            wait(&st);
            if (WIFEXITED(st))
            {
                return WEXITSTATUS(st);
            }
            else
            {
                return WTERMSIG(st) + 128;
            }
        }
        else
        {
            return -1;
        }
    }
    return 0;
}

int main()
{
    while (1)
    {
        Print_Menu();

        char* ARGV[INPUT_MAX] = { 0 };

        char arr_input[INPUT_MAX] = { 0 };

        int num = Partition(ARGV, arr_input);

        if(num == 0) continue;

        lastcode = Branch(ARGV, num);
    }
    return 0;
}
